
/**
 * to invalidate the winsock.h in windows.h file
 */
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>

#include <mmsystem.h>
#include "myfilelog.hxx"
#include "loghelper.hxx"
#include "wfaudioplayer.hxx"

WFAudioPlayer::WFAudioPlayer(tUInt32 bits_sample,tUInt32 sample_sec,tUInt32 block_nb,tUInt32 block_size,tUInt32 channel_nb,FileLog* filelog)
		:	AudioPlayer(bits_sample,sample_sec,block_nb,block_size,channel_nb,filelog),
			_hwaveout(NULL),
			_blocks(NULL),
			_pbuf(NULL),
			_buf_size(0),
			_all_played_nb(0)
{}

//\brief Descontructor
WFAudioPlayer::~WFAudioPlayer()
{
}

tInt32 WFAudioPlayer::Init()
{
	AllocateBlocks(_block_nb,_block_size);

	MMRESULT mmr;
	WAVEFORMATEX wfx; 

	wfx.cbSize = 0;
	wfx.wFormatTag = WAVE_FORMAT_PCM;
	wfx.nChannels = _channel_nb;;
	wfx.wBitsPerSample = _bits_per_sample;
	wfx.nSamplesPerSec = _samples_per_sec;
	wfx.nBlockAlign = wfx.nChannels * (wfx.wBitsPerSample >> 3);
	wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;

	 if(mmr = waveOutOpen(&_hwaveout,
					WAVE_MAPPER,
					&wfx,
					(DWORD_PTR)WaveOutProc,
					(DWORD_PTR)this,
					CALLBACK_FUNCTION		)	!=	MMSYSERR_NOERROR)
	 {
		 LOGERROR("WFAudioPlayer::Init:Failed to open wave out with returned value:"<<mmr);

		 return -1;
	 }

	return 1;
}

tInt32 WFAudioPlayer::UnInit()
{
	return 0;
}

/**
 * Write voice data into player, as writed, it plays
 */
tInt32 WFAudioPlayer::WriteAudio(tByte* data, tUInt32 size)
{
    WAVEHDR* current = &_blocks[_current_block];

	/*
	* if no free block to write into, we don't wait, just return, abort writting.
	*/
	if (DecrFreeBlockCount() < 0)
	{
		LOGDEBUG("WFAudioPlayer::WriteAudio:No free block for writting,just abandon the voice data.");
		return -1;
	}
	
	/* 
	* first make sure the header we're going to use is unprepared
	*/
	if(current->dwFlags & WHDR_PREPARED)
	{
		if(MMSYSERR_NOERROR == waveOutUnprepareHeader(_hwaveout, current, sizeof(WAVEHDR)))
		{
			LOGDEBUG("WFAudioPlayer::WriteAudio:waveOutUnprepareHeader block #"<<_current_block<<" succeeded ...");
		}
		else
		{
			LOGERROR("WFAudioPlayer::WriteAudio:waveOutUnprepareHeader failed at  block #"<<_current_block<<",_free_block_count="<<_free_block_count);
		}
	}

	memcpy(current->lpData, data, size);
	current->dwBufferLength = size;

	//LOGDEBUG("WFAudioPlayer::WriteAudio:Write the following data to block #"<<_current_block);
	//for (int i=0; i<current->dwBufferLength; i+=4)
	//{
	//	//LOGDEBUG("WriteAudio:address:"<<(tUInt32)(current->lpData+i)<<"\tdata:"<<*(tInt32*)(data+i));
	//
	//	//LOGDEBUG("WriteAudio:address:");
	//}

	if (MMSYSERR_NOERROR == waveOutPrepareHeader(_hwaveout, current, sizeof(WAVEHDR)) && 
	MMSYSERR_NOERROR == waveOutWrite(_hwaveout, current, sizeof(WAVEHDR)))
	{
		LOGDEBUG("WFAudioPlayer::WriteAudio:waveOutWrite to block #"<<_current_block<<" succeeded");
	}
	else
	{
		LOGERROR("WFAudioPlayer::WriteAudio:waveOutWrite failed at block#"<<_current_block<<" _free_block_count:"<<_free_block_count);
	}

	/*
	* Move current block forward.
	*/
	_current_block++;
	_current_block %= _block_nb;

	return 1;
}
tInt32 WFAudioPlayer::SetCurrentPos(void)
{
	MMRESULT mmr;
	MMTIME  mmt;
	tUInt32 current_block = 0;

	//current_block = _current_block;

	mmt.wType = TIME_BYTES;
	
	mmr = waveOutGetPosition(
							_hwaveout, 
							&mmt, 
							sizeof(MMTIME));

	if (mmr == MMSYSERR_NOERROR)
	{
		if (mmt.u.cb == 0)
		{
			_all_played_nb = 0;
			_current_pos = -1;			
		}
		else
		{
			_all_played_nb = mmt.u.cb;
			_current_pos = mmt.u.cb % (_block_nb*_block_size);

			LOGDEBUG("WFAudioPlayer::SetCurrentPos:_current_pos="<<_current_pos<<",_all_played_nb="<<_all_played_nb);
		}
	}
	else
	{
		_current_pos = -1;

		LOGERROR("WFAudioPlayer::SetCurrentPos:waveOutGetPosition failed with mmr="<<mmr);
	}

	return _current_pos;
}
void WFAudioPlayer::ResetCurrentPos(void)
{
	_current_pos = -1;
}
tByte* WFAudioPlayer::GetLatestData(tInt32 size)
{
	if (_current_pos == -1 || _all_played_nb < size)
		return NULL;

	tByte* echo = NULL;

	if (_current_pos >= size)
		echo =  (tByte *)(_blocks[0].lpData+_current_pos-size);
	else
	{
		if (_buf_size < size)
		{
			if (_pbuf)
				delete _pbuf;

			_pbuf = new tByte[size];
			_buf_size = size;
		}

		memcpy(_pbuf,_blocks[_block_nb-1].lpData+(_block_size-(size-_current_pos)),(size-_current_pos));
		memcpy(_pbuf+(size-_current_pos),_blocks[0].lpData,_current_pos);

		echo = _pbuf;
	}

	LOGDEBUG("WFAudioPlayer::GetLatestData("<<size<<") while _current_pos="<<_current_pos<<",_all_played_nb="<<_all_played_nb<<",_free_block_count="<<_free_block_count);
	//for (int i=0; i<size; i+=4)
	//{
	//	//LOGDEBUG("GetLatestData: address:"<<(tUInt32)(echo+i)<<"\tdata:"<<*(tInt32*)(echo+i));
	//}
	
	return echo;
}
tByte* WFAudioPlayer::AllocateBlocks(tUInt32 count, tUInt32 size)
{
	tByte* buffer;

	tUInt32 totalBufferSize = (size + sizeof(WAVEHDR)) * count;

	LOGDEBUG("WFAudioPlayer::AllocateBlocks started");


    /**
     * allocate memory for the entire set in one go
     */
	if (NULL == (buffer = (tByte*)malloc(totalBufferSize*sizeof(tByte))))
	{
		LOGERROR("WFAudioRecorder::AllocateBlocks:HeapAlloc failed.");
        return NULL;
	}
	memset((void*)buffer, 0x00, totalBufferSize);

    /**
     * and set up the pointers to each bit
     */
    _blocks = (WAVEHDR*)buffer;
    buffer += sizeof(WAVEHDR) * count;

    for(tUInt32 i = 0; i < count; i++) 
	{
		_blocks[i].dwBufferLength = size;
		_blocks[i].lpData = (LPSTR)buffer;
		buffer += size;

		LOGDEBUG("WFAudioPlayer::AllocateBlocks:Block #"<<i<<" address starts at "<<(tUInt32)_blocks[i].lpData);
	}

	LOGDEBUG("WFAudioPlayer::AllocateBlocks:Ended");

    return (tByte*)_blocks;
}
void WFAudioPlayer::FreeBlocks(void)
{
	/* 
    * and this is why allocateBlocks works the way it does
    */ 
    if (_blocks)
	{
		free(_blocks);
		_blocks = NULL;
	}
}


void __stdcall WFAudioPlayer::WaveOutProc(HWAVEOUT hWaveOut,UINT uMsg,DWORD dwInstance,DWORD dwParam1,DWORD dwParam2)
{
	if(uMsg != WOM_DONE)
		return;

	WFAudioPlayer* player = (WFAudioPlayer*)dwInstance;

	if (player)
	{
		/**
		 * A headerwave playing is over, so increase Free Blcok Count
		 */
		player->IncrFreeBlockCount();
	}
}
void WFAudioPlayer::ResetPlayer(void)
{
	MMRESULT mmr;

	_free_block_count = _block_nb;

	_current_block = 0;

	_current_pos = -1;

	mmr = waveOutReset(_hwaveout);

	if (mmr != MMSYSERR_NOERROR)
	{
		LOGERROR("WFAudioRecorder::ResetPlayer:waveOutReset failed with mmr="<<mmr);
	}

	LOGDEBUG("WFAudioPlayer::ResetPlayer:Player reseted.");
}